#include "access.h"
#include "dlist.h"
#include "hash_table.h"
#include "mem_alloc.h"

#ifndef _WIN32
#include <pthread.h>
#else
#include <winsock2.h>
#include <windows.h>
#endif

#include <string.h>

#ifndef _WIN32
static void lock(pthread_mutex_t * mutex)
{
    pthread_mutex_lock(mutex);
}

static void unlock(pthread_mutex_t * mutex)
{
    pthread_mutex_unlock(mutex);
}
#else

static void lock(HANDLE  mutex)
{
	WaitForSingleObject(mutex, INFINITE);
}

static void unlock(HANDLE  mutex)
{
    ReleaseMutex(mutex);
}
#endif

static dlist_node * get_list_node(void * data)
{
    dlist_node * p = GET_OBJECT(dlist_node);
    if (p) {
        p->next = p->prev = 0;
        p->data = data;
    }
    return p;
}

static void release_list_node(dlist_node * node)
{
   RELEASE_OBJECT(dlist_node, node);
}

struct _access
{
    int max_count_per_conn;
    hash_table * map;
#ifdef _WIN32
	HANDLE mutex;
#else
    pthread_mutex_t mutex;
#endif
    func_open  open;
    func_close close;
};

Access * access_create(func_open opan,
                       func_close cloze,
                       int max_count_per_conn)
{
	Access * acc;
    if (max_count_per_conn <= 0 || opan == 0) {
        return 0;
    }

    acc = malloc(sizeof(Access));
    if (acc) {
        acc->map = str_hash_table_create(16, 0);
        if (acc->map == 0) {
            free(acc);
            return 0;
        }
#ifdef _WIN32
		acc->mutex = CreateMutex(NULL, FALSE, NULL);
		if (acc->mutex == NULL) {
			hash_table_destory(acc->map);
            free(acc);
            return 0;
		}
#else
        if (pthread_mutex_init(&acc->mutex, 0) != 0) {
            hash_table_destory(acc->map);
            free(acc);
            return 0;
        }
#endif
        acc->max_count_per_conn = max_count_per_conn;
        acc->open = opan;
        acc->close = cloze;
    }
    return acc;
}


static int release_ptr(Access * acc, const char * key, void * conn)
{
	hash_item * ite;
	dlist_node * node;
    dlist_node * head;

    lock(&acc->mutex);
    ite = hash_table_find(acc->map, key);
    if (ite == 0) {
        unlock(&acc->mutex);
        return -1;
    }

    node = get_list_node(conn);
    head = (dlist_node*)(ite->data);
    dlist_insert_tail(head, node);
    ite->data = head;
    unlock(&acc->mutex);
    return 0;
}

static void * get_ptr(Access * acc, const char * key, int * need_open)
{
    void * conn = 0;
    dlist_node * head = 0;

    int insert = 0;
	hash_item * ite;

    lock(&acc->mutex);
    ite = hash_table_find_or_insert(acc->map, key, &insert);
    if (insert != 0) {
        // 建数据结构，不进行实际连接
        int i;
        for (i = 0; i < acc->max_count_per_conn; i++) {
            dlist_node * p = get_list_node(0);
            dlist_insert_tail(head, p);
        }
        ite->data = head;
    }  else {
        head = (dlist_node*)ite->data;
    }

    if(need_open) *need_open = 0;
    //获取list中第一个
    if (head != 0) {
        dlist_node * cur = head;
        conn = cur->data;
        dlist_remove(head, cur);
        cur->next = cur->prev = 0;
        release_list_node(cur);
        ite->data = head;
        if ((conn == 0) && need_open) {
            *need_open = 1;
        }
    }
    unlock(&acc->mutex);
    return conn;
}

void * access_get(Access * acc, const char * key)
{
	int need_open = 0;
	void * obj;
    if (acc == 0 || key == 0) {
        return 0;
    }


    obj = get_ptr(acc, key, &need_open);

    if (need_open == 0) {
        return obj;
    }

    if (acc->open) {
        obj = acc->open(key);
    }

    if (obj == 0) {
        release_ptr(acc, key, 0);
    }
    return obj;
}

int access_release(Access * acc, const char * key, void * obj)
{
    if (acc == 0 || key == 0 || obj == 0) {
        return -1;
    }
    return release_ptr(acc, key, obj);
}

int access_close(Access * acc, const char * key, void * obj)
{
    if (acc == 0 || key == 0 || obj == 0) {
        return -1;
    }

    if (acc->close) acc->close(obj);
    return release_ptr(acc, key, 0);
}

void access_destory(Access * acc)
{
    hash_item * ite = 0;
    while ((ite = hash_table_next(acc->map, ite)) != 0) {
        dlist_node * head = (dlist_node*)ite->data;
        ite->data = 0;

        while (head) {
            dlist_node * cur = head;
            dlist_remove(head, cur);
            
            if (cur->data && acc->close) {
                acc->close(cur->data);
            }
            release_list_node(cur);
        }
    }

    if (acc->map) hash_table_destory(acc->map);
#ifdef _WIN32
	CloseHandle(acc->mutex);
#else
    pthread_mutex_destroy(&acc->mutex);
#endif
    free(acc);
}
